﻿using NUnit.Framework;
using Rhino.Mocks;
using System;

namespace NUnitTestBase
{
    /// <summary>
    /// NUnitTestBase class for helping unit test development with NUnit.
    /// </summary>
    /// <typeparam name="T">Target object type which need to be tested.</typeparam>
    [TestFixture]
    public abstract class NUnitTestBase<T>
    {
        #region Events

        public delegate void TestCleanUpNotification(object sender, TestCleanUpNotificationData args);

        public event TestCleanUpNotification CleanTestData;

        #endregion
        #region Fields

        /// <summary>
        /// Rhino mocking framework object
        /// </summary>
        private MockRepository mocks;

        /// <summary>
        /// Component object which represents class object which need to be tested.
        /// </summary>
        private T component;

        #endregion

        #region Properties

        /// <summary>
        /// Component object which represents class object which need to be tested.
        /// </summary>
        public T Component
        {
            get
            {
                return component;
            }
        }

        /// <summary>
        /// Rhino mocking framework object.
        /// </summary>
        protected MockRepository Mocks
        {
            get
            {
                return mocks;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor.
        /// </summary>
        protected NUnitTestBase()
        {
            InitilizeMocks();
            InitilizeComponent();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initializes mocking framework.
        /// </summary>
        private void InitilizeMocks()
        {
            mocks = new MockRepository();
        }

        /// <summary>
        /// Initializes test class object from child.
        /// </summary>
        private void InitilizeComponent()
        {
            component = this.CreateChildComponent();
        }

        /// <summary>
        /// Method executes before each test.
        /// </summary>
        [SetUp]
        protected virtual void BeforeEachTest()
        {
            
        }

        /// <summary>
        /// Calls end of each Test. This implements TearDown method for TextFixture.
        /// This method will trigger custom cleanup event if subscribed.
        /// </summary>
        [TearDown]
        protected virtual void AfterEachTest()
        {
            if (CleanTestData != null)
            {
                string testName = TestContext.CurrentContext.Test.Name;
                CleanTestData(this, new TestCleanUpNotificationData { TestMethodName = testName });
            }
        }

        /// <summary>
        /// This method executes before all test but only once in life time of derived object.
        /// </summary>
        [TestFixtureSetUp]
        protected virtual void BeforeAllTests()
        {
        }

        /// <summary>
        /// This method executes after all test but only once in life time of derived object.
        /// </summary>
        [TestFixtureTearDown]
        protected virtual void AfterAllTests()
        {
            this.DisposeBase();
        }

        /// <summary>
        /// Creates an object for test.
        /// </summary>
        /// <returns>New object for test.</returns>
        protected abstract T CreateComponent();

        /// <summary>
        /// Creates Component object.
        /// </summary>
        /// <returns></returns>
        private T CreateChildComponent()
        {
            component = CreateComponent();
            return component;
        }

        /// <summary>
        /// This method can be used for clean up test data.
        /// Any custom cleanup argument need to be cleared using this method.
        /// </summary>
        protected  virtual void DisposeBase()
        {
        }
        #endregion
    }
}
